﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Input;
using NanoMvvm.Threading;

namespace NanoMvvm
{
    public class AsyncCommand<T> : CommandWrapperBase<T> where T : CommandBase
    {
        private volatile bool isExecuting;
        private volatile bool isCancelRequested;

        private readonly Action<IEnumerable<Exception>> onError; 

        public AsyncCommand(T command, Action<IEnumerable<Exception>> onErrorHandler = null) : base(command)
        {
            onError = onErrorHandler;
        }

        public bool IsExecuting
        {
            get { return isExecuting; }
            private set { isExecuting = value; }
        }

        public bool IsCancelRequested
        {
            get { return isCancelRequested; }
            private set { isCancelRequested = value; }
        }

        public void RequestCancel()
        {
            IsCancelRequested = true;
        }

        protected override void Execute(object parameter)
        {
            IsCancelRequested = false;
            IsExecuting = true;

            RaiseCanExecuteChanged();

            Task.Factory.StartNew(base.Execute, parameter)
                .ContinueWith(t =>
                                  {
                                      IsExecuting = false;
                                      RaiseCanExecuteChanged();

                                      if(t.Exception != null && onError != null)
                                      {
                                          if (onError != null)
                                              onError(t.Exception.InnerExceptions);
                                          else
                                              throw t.Exception.Flatten();
                                      }
                                  }, new SynchronizationContextTaskScheduler(SynchronizationContext));
        }

        protected override bool CanExecute(object parameter)
        {
            return (!IsExecuting && ((ICommand)WrappedCommand).CanExecute(parameter));
        }
    }
}